package titt.server.func;

import titt.head.HeadFactory;
import titt.head.request.MethodHead;
import titt.head.request.form.FormHead;
import titt.head.response.ResponseHead;
import titt.io.reader.POSTReader;
import titt.io.socket.TITTSocket;
import titt.io.writer.ResponseWriter;
import titt.key.KeyFactory;
import titt.key.TITTKey;
import titt.server.os.ObjectServer;
import titt.server.os.ServerValueConfig;
import titt.server.service.LinkedService;
import titt.server.service.Service;
import titt.server.service.ServiceImpl;
import titt.server.service.invoker.TITTInternalInvoker;
import titt.server.service.listener.SwitchServiceListener;
import titt.service.TITTInternal;
import titt.type.TypedObject;
import titt.type.Types;

public class ServiceRegistration implements TITTFunction {
	
	public String getMethodName() {
		return "register";
	}

	public void handle(TITTKey key, MethodHead head, TITTSocket ts, ObjectServer os) throws Exception {
		
		if(os.getServerDatabase().containsDuplicateKey(key)) {
			ts.getHeadWriter().writeHead(new ResponseHead(409, HeadFactory.EMPTY));
			ts.close();
			return;
		}
			
		
		String serviceID = "SERV-" + os.generateID(true);
		
		key = KeyFactory.createKey(key.getTokens());
		
		
		POSTReader pr = new POSTReader(ts, os.getEncodingFacade(), new FormHead(head));
		
		TypedObject[] args = pr.readValues();
		
		String[][] registrationInterface = args[0].getObject();

		Service linked = null, s = null;

		if(args.length > 1) {
			String link = args[1].getObject();
			linked = os.getServerDatabase().get(link);
			s = new LinkedService(key, serviceID, registrationInterface, linked);
		}
		else
			s = new ServiceImpl(os, key, serviceID, registrationInterface);
		
		os.getServerDatabase().add(s);

		//try catch below to make sure heartbeat gets inited
		
		ResponseWriter rw = new ResponseWriter(ts, os.getEncodingFacade(), new ResponseHead(200, HeadFactory.EMPTY));
		rw.writeValues(new TypedObject(Types.STRING, serviceID));
		
		ts.close();
		
		if(linked != null) {
			SwitchServiceListener listener = new SwitchServiceListener();

			s.attachServiceListener(listener);
			
			listener.waitForServiceStop();
			
			s.removeServiceListener(listener);
		}
		else
			heartbeat(serviceID, os);

		os.getServerInternal().unregister(serviceID, 0);
	}

	private void heartbeat(String id, ObjectServer os) {

		TITTInternalInvoker internal = new TITTInternalInvoker(id, os);
		
		long lastSuccess = 0;

		while(true) {
			try {

				Service s = os.getServerDatabase().get(id);
				
				if(s == null)
					break;				

				while((lastSuccess = System.currentTimeMillis() - Math.max(s.lastSuccess(), lastSuccess)) < ServerValueConfig.get().getTimeout())
					Thread.sleep(ServerValueConfig.get().getTimeout() - lastSuccess);
				

				int ping = internal.call(TITTInternal.PING);

				if(ping != 42) break;

				lastSuccess = System.currentTimeMillis();

			}
			catch(Exception e) {
				break;
			}
		}
		
	}

}
